package top.yangguangmc.sunshine_anticheat.utils;

import java.util.*;

/**
 * A useful timer to check passed time and record the checks.
 */
@SuppressWarnings("unused")
public class Timer {
    /**
     * The time when the timer starts timing.
     */
    private long startTime;
    /**
     * A list of timestamps.
     * Adds an element after a check.
     */
    private final List<Long> checks = new LinkedList<>();

    /**
     * Uses the current time as the start time to create a new instance.
     */
    public Timer() {
        this.startTime = System.currentTimeMillis();
    }

    /**
     * Uses a specific time as the start time to create a new instance.
     *
     * @param startTime The difference, measured in milliseconds, between the specific time and midnight, January 1, 1970.
     */

    public Timer(long startTime) {
        this.startTime = startTime;
    }

    /**
     * Uses a specific time as the start time to create a new instance.
     *
     * @param startDate Start time, use a {@code Date} instance.
     */

    public Timer(Date startDate) {
        this.startTime = startDate.getTime();
    }

    /**
     * Uses a specific time as the start time to create a new instance.
     *
     * @param startCalendar Start time, use a {@code Calendar} instance.
     */

    public Timer(Calendar startCalendar) {
        this.startTime = startCalendar.getTime().getTime();
    }

    public long getStartTime() {
        return startTime;
    }

    public Date getStartTimeAsDate() {
        return new Date(startTime);
    }

    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }

    public void setStartTime(Date date) {
        this.startTime = date.getTime();
    }

    public void setStartTime(Calendar calendar) {
        this.startTime = calendar.getTime().getTime();
    }

    /**
     * Check the time between the start time and now in millis. And then record the current timestamp.
     *
     * @return The time between the start time and now. (ms)
     */
    public long check() {
        checks.add(System.currentTimeMillis());
        return System.currentTimeMillis() - startTime;
    }

    /**
     * Check the time between the start time and now in seconds. And then record the current timestamp.
     *
     * @return The time between the start time and now. (s)
     */
    public double checkSeconds() {
        checks.add(System.currentTimeMillis());
        long result = System.currentTimeMillis() - startTime;
        return (double) result / 1000;
    }

    /**
     * Gets the recorded timestamps.
     * <b>Notice: this will returns a copy, not the original instance.</b>
     *
     * @return A list of timestamps.
     */
    public List<Long> getChecks() {
        return new ArrayList<>(checks);
    }

    /**
     * Gets the recorded timestamps.
     * <b>Notice: this will returns a copy, not the original instance.</b>
     *
     * @return An array of timestamps.
     */
    public long[] getChecksAsArray() {
        Object[] array = checks.toArray();
        long[] result = new long[array.length];
        for (int i = 0; i < array.length; i++) {
            Object obj = array[i];
            if (obj instanceof Long) {
                Long longObj = (Long) obj;
                result[i] = longObj;
            }
        }
        return result;
    }

    public int getCheckCount() {
        return checks.size();
    }

    /**
     * Reset this timer to the current time.
     */
    public void reset() {
        checks.clear();
        this.startTime = System.currentTimeMillis();
    }

    /**
     * Reset this timer to the specific time.
     */
    public void reset(long newStartTime) {
        checks.clear();
        this.startTime = newStartTime;
    }

    /**
     * Reset this timer to the specific time.
     */
    public void reset(Date newStartTime) {
        checks.clear();
        this.startTime = newStartTime.getTime();
    }

    /**
     * Reset this timer to the specific time.
     */
    public void reset(Calendar newStartTime) {
        checks.clear();
        this.startTime = newStartTime.getTime().getTime();
    }

    /**
     * Checks whether the time has passed an enough time.
     *
     * @param ms The specific time.
     * @return If it has passed the specific time, returns true.
     */
    public boolean hasTimePassed(long ms) {
        if (ms <= 0) return true;
        return System.currentTimeMillis() - startTime >= ms;
    }

    @Override
    public String toString() {
        return super.toString() + "{startTime = " + startTime + ", checksCount = " + checks.size() + "}";
    }
}
